#include "Timer.h"

#define CLOCKID CLOCK_REALTIME
#define SIG SIGUSR1

namespace TimerNameSpace {

/**
 * construct a timer object and initiate the timeout handler
 * timeHandler - a pointer to the object that implements the timer timeout handler interface
 */
Timer::Timer( TimerTimeoutHandler * timeHandler )
{
    timeOutHandlerImp = timeHandler;
    m_Duration = 0;

    TimerHandler handler_cb = &timeOutHandler;
    createTimer( &timerid, handler_cb );
}

Timer::~Timer()
{
    stopTimer( timerid );
}

/**
 * sets the timer duration
 * seconds- the time to be set in seconds
 */
void Timer::setDuration(long int seconds)
{
    m_Duration = seconds;
}

/**
 * starts the timer
 */
void Timer::start()
{
    startTimer(timerid, m_Duration, 3);
}

/**
 * restart the timer
 */
void Timer::restart()
{
    stopTimer(timerid);
    startTimer(timerid, m_Duration, 0);
}

/**
 * stops the timer
 */
void Timer::stop()
{
    stopTimer(timerid);
}

/**
 * create a new timer
 */
void Timer::createTimer(timer_t *timerid, TimerHandler handler_cb)
{
    sigevent sev;
    pthread_attr_t attr;
    pthread_attr_init( &attr );
    sched_param parm;

    parm.sched_priority = 255;
    pthread_attr_setschedparam(&attr, &parm);

    sev.sigev_notify_attributes = &attr;
    sev.sigev_notify = SIGEV_THREAD;
    sev.sigev_notify_function = handler_cb;
    sev.sigev_signo = SIG;
    sev.sigev_value.sival_ptr = this;

    timer_create(CLOCKID, &sev, timerid);
}

void Timer::startTimer(timer_t timerid, int startTimeout, int cyclicTimeout)
{
    itimerspec its;

    /* Start the timer */
    its.it_value.tv_sec = startTimeout;
    its.it_value.tv_nsec = 0;

        /* for cyclic timer */
    its.it_interval.tv_sec = cyclicTimeout;
    its.it_interval.tv_nsec = 0;

    timer_settime(timerid, 0, &its, NULL);
}

void Timer::stopTimer(timer_t timerid)
{
    itimerspec its;
    its.it_value.tv_sec = 0;
    its.it_value.tv_nsec = 0;
    its.it_interval.tv_sec = 0;
    its.it_interval.tv_nsec = 0;
    timer_settime(timerid, 0, &its, NULL);
}

void Timer::timeOutHandler( sigval_t This )
{
    Timer * timer = (Timer*) This.sival_ptr;
    timer->callbackWrapper();
}

void Timer::callbackWrapper( void )
{
    timeOutHandlerImp->handlerFunction();
    stopTimer( timerid );
}

}
